package lists;

import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.RandomAccess;

public class MyArrayList<T> implements MyList<T>, RandomAccess {
	private T[] array;
	private int size;

	@SuppressWarnings("unchecked")
	public MyArrayList() {

		array = (T[]) new Object[10];
	}

	@SuppressWarnings("unchecked")
	public MyArrayList(int initialCapacity) {
		if (initialCapacity < 0) {
			try {
				throw new MyIndexOutOfBoundsException(
						"Incorrect initialCapacity!");
			} catch (MyIndexOutOfBoundsException e1) {
				System.out.println(e1.getMessage());
				throw new NullPointerException();
			}
		}
		array = (T[]) new Object[initialCapacity];
	}

	@SuppressWarnings("unchecked")
	public MyArrayList(MyList<T> list) {
		if (list.size() == 0) {
			array = (T[]) new Object[10];
		} else {
			array = (T[]) new Object[list.size()];
			Object[] listArr = list.toArray();
			System.arraycopy(listArr, 0, array, 0, listArr.length);
			size = list.size();
		}
	}

	private void ensureCapacity(int number) {

		if (array.length <= number) {

			@SuppressWarnings("unchecked")
			T[] newArray = (T[]) new Object[(array.length * 3) / 2 + 1];
			System.arraycopy(array, 0, newArray, 0, size);
			array = newArray;
		} else {
			return;
		}

	}

	protected void expandCapacity(int minCapacity) {
		if (array.length >= minCapacity) {
			return;
		}
		@SuppressWarnings("unchecked")
		T[] newArray = (T[]) new Object[minCapacity + 1];
		System.arraycopy(array, 0, newArray, 0, size);
		array = newArray;

	}

	public void trimToSize() {
		@SuppressWarnings("unchecked")
		T[] newArray = (T[]) new Object[size];
		System.arraycopy(array, 0, newArray, 0, size);
		array = newArray;
	}

	@Override
	public void add(T e) {
		ensureCapacity(size + 1);
		array[size++] = e;

	}

	@Override
	public void add(int index, T e) {
		if (index > size || index < 0) {
			try {
				throw new MyIndexOutOfBoundsException("Incorrect index!");
			} catch (MyIndexOutOfBoundsException e1) {
				System.out.println(e1.getMessage());
				throw new NullPointerException();
			}
		}

		ensureCapacity(size + 1);
		System.arraycopy(array, index, array, index + 1, size - index);
		array[index] = e;
		size++;

	}

	@Override
	public void addAll(Collection<? extends T> e) {
		Iterator<? extends T> iter = e.iterator();
		while (iter.hasNext()) {
			add(iter.next());
		}

	}

	@Override
	public void addAll(int index, Collection<? extends T> e) {
		if (index > size || index < 0) {
			try {
				throw new MyIndexOutOfBoundsException("Incorrect index!");
			} catch (MyIndexOutOfBoundsException e1) {
				System.out.println(e1.getMessage());
				throw new NullPointerException();
			}
		}
		expandCapacity(size + e.size());
		Object[] col = e.toArray();
		System.arraycopy(array, index, array, index + e.size(), size - index);
		System.arraycopy(col, 0, array, index, col.length);
		size = size + e.size();
	}

	@Override
	public T get(int index) {
		if (index >= size || index < 0) {
			try {
				throw new MyIndexOutOfBoundsException("Incorrect index!");
			} catch (MyIndexOutOfBoundsException e1) {
				System.out.println(e1.getMessage());
				throw new NullPointerException();
			}
		}
		return array[index];
	}

	@Override
	public T remove(int index) {
		if (index >= size || index < 0) {
			try {
				throw new MyIndexOutOfBoundsException("Incorrect index!");
			} catch (MyIndexOutOfBoundsException e1) {
				System.out.println(e1.getMessage());
				throw new NullPointerException();
			}
		}

		T element = array[index];
		System.arraycopy(array, index + 1, array, index, size - index - 1);
		array[size--] = null;
		return element;
	}

	@SuppressWarnings("unchecked")
	@Override
	public void clear() {
		array = (T[]) new Object[10];
		size = 0;
	}

	@Override
	public boolean isEmpty() {

		return size == 0;
	}

	@Override
	public void set(int index, T e) {
		if (index >= size || index < 0) {
			try {
				throw new MyIndexOutOfBoundsException("Incorrect index!");
			} catch (MyIndexOutOfBoundsException e1) {
				System.out.println(e1.getMessage());
				throw new NullPointerException();
			}
		}
		array[index] = e;

	}

	@Override
	public int indexOf(Object e) {
		if (e == null) {
			int i = 0;
			while ((i < size - 1) && (array[i] != e)) {
				i++;
			}
			if (array[i] == e) {
				return i;
			}
			return -1;
		} else {
			int i = 0;
			while ((i < size - 1) && (!e.equals(array[i]))) {
				i++;
			}
			if (e.equals(array[i])) {
				return i;
			}
			return -1;
		}

	}

	@Override
	public int size() {

		return size;
	}

	@Override
	public Object[] toArray() {
		if (size == 0) {
			return null;
		}
		Object[] arr = new Object[size];
		System.arraycopy(array, 0, arr, 0, size);

		return arr;
	}

	@Override
	public Iterator<T> iterator() {

		return new Iterator<T>() {
			private int count;

			@Override
			public boolean hasNext() {

				return count < size;
			}

			@Override
			public T next() {
				if (hasNext()) {
					return get(count++);
				}
				throw new NoSuchElementException();
			}

			@Override
			public void remove() {
				throw new UnsupportedOperationException();

			}
		};
	}

}
